﻿// =================================================================================
// Copyright © 2004-2011 Matt Sollars
// All rights reserved.
// 
// This code and information is provided "as is" without warranty of any kind,
// either expressed or implied, including, but not limited to, the implied 
// warranties of merchantability and/or fitness for a particular purpose.
// =================================================================================
using System;
using System.Text;

using SecuritySwitch.Abstractions;
using SecuritySwitch.Configuration;


namespace SecuritySwitch.Evaluation
{
    /// <summary>
    /// The default implementation of ISecurityEnforcer.
    /// </summary>
    public class SecurityEnforcer : ISecurityEnforcer
    {

        private static bool IsSecureConnection(HttpRequestBase request, Settings settings)
        {
            if (string.IsNullOrEmpty(settings.OffloadedSslHeaders) || request.IsSecureConnection || request.Headers == null) return request.IsSecureConnection;
            System.Collections.Specialized.NameValueCollection headers = System.Web.HttpUtility.ParseQueryString(settings.OffloadedSslHeaders);
            foreach (string name in headers.AllKeys)
            {
                if (request.Headers[name] == null) continue;
                if (string.IsNullOrEmpty(headers[name]) || string.Equals(headers[name], request.Headers[name], StringComparison.OrdinalIgnoreCase))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Gets any URI for the specified request that ensures it is being accessed by the proper protocol, if a match is found in the settings.
        /// </summary>
        /// <param name="request">The request to ensure proper access for.</param>
        /// <param name="response">The response to use if a redirection or other output is necessary.</param>
        /// <param name="security">The security setting to match.</param>
        /// <param name="settings">The settings used for any redirection.</param>
        /// <returns>A URL that ensures the requested resources matches the specified security; or null if the current request already does.</returns>
        public string GetUriForMatchedSecurityRequest(HttpRequestBase request, HttpResponseBase response, RequestSecurity security, Settings settings)
        {
            string targetUrl = null;

            if (security == RequestSecurity.Secure && !IsSecureConnection(request, settings) || security == RequestSecurity.Insecure && IsSecureConnection(request, settings))
            {
                // Determine the target protocol and get any base target URL from the settings.
                string targetProtocolScheme;
                string baseTargetUrl;
                if (security == RequestSecurity.Secure)
                {
                    targetProtocolScheme = Uri.UriSchemeHttps;
                    baseTargetUrl = settings.BaseSecureUri;
                }
                else
                {
                    targetProtocolScheme = Uri.UriSchemeHttp;
                    baseTargetUrl = settings.BaseInsecureUri;
                }

                if (string.IsNullOrEmpty(baseTargetUrl))
                {
                    // If there is no base target URI, just switch the protocol scheme of the current request's URI.
                    // * Account for cookie-less sessions by applying the application modifier.
                    targetUrl = targetProtocolScheme + Uri.SchemeDelimiter + request.Url.Authority + response.ApplyAppPathModifier(request.RawUrl);
                }
                else
                {
                    // Build the appropriate URI based on the specified target URL.
                    var uri = new StringBuilder(baseTargetUrl);

                    // - Use the full request path, but remove any sub-application path.
                    uri.Append(request.RawUrl);
                    if (request.ApplicationPath.Length > 1)
                    {
                        uri.Remove(baseTargetUrl.Length, request.ApplicationPath.Length);
                    }

                    // Normalize the URI.
                    uri.Replace("//", "/", baseTargetUrl.Length - 1, uri.Length - baseTargetUrl.Length);

                    targetUrl = uri.ToString();
                }
            }

            return targetUrl;
        }
    }
}